Files = (function () {
    function join() {
        var paths = Array.prototype.slice.call(arguments).map(function (f) {
            return f.path();
        });
        return Files_join.apply(this, paths);
    }

    function filter(filter) {
        return function (file) {
            return filter(file.path());
        };
    }

    function path() {
        return function (file) {
            return file.path();
        }
    }

    return {
        join: join,
        filter: filter,
        path: path
    };
})();

(function (key) {
    file = function (/* ...parts */) {
        var file = Object.create(File.prototype);
        var temp = File.apply(file, Array.prototype.slice.call(arguments));
        return (temp != undefined ? temp : file);
    };

    File = function (/* ...parts */) {
        var state = {};

        this.private = function (accessor) {
            return accessor === key && state;
        };

        var args = Array.prototype.slice.call(arguments).map(function (arg) {
            return arg instanceof File ? arg.path() : arg;
        });
        state.path = Files_path.apply(null, args); // <-- null because Files_path accesses the top scope. (global)
        state.path = state.path.replace(/\\/g, "/");
    };

    File.prototype.path = function () {
        return this.private(key).path;
    };

    File.prototype.name = function () {
        var tmp = this.path();
        var i = tmp.lastIndexOf("/");
        return i != -1 ? tmp.substring(i + 1) : tmp;
    };

    File.prototype.create = function (/* ...parts */) {
        var file = Object.create(File.prototype);
        var temp = File.apply(file, [this].concat(Array.prototype.slice.call(arguments)));
        return (temp != undefined ? temp : file);
    };

    File.prototype.exists = function () {
        return Files_exists(this.path());
    };

    File.prototype.isDirectory = function () {
        return Files_isDirectory(this.path());
    };

    File.prototype.isFile = function () {
        return Files_isFile(this.path());
    };

    File.prototype.parent = function () {
        return Files_parent(this.path());
    };

    File.prototype.list = function () {
        return Files_list(this.path()).map(function (path) {
            return file(path);
        });
    };

    File.prototype.makeDir = function () {
        console.log("making directory", this.path());
        return Files_makeDir(this.path());
    };

    File.prototype.remove = function () {
        console.log("removing", this.path());
        return Files_remove(this.path());
    };

    File.prototype.copyTo = function (dest) {
        if (!this.isFile()) throw new Error(this.path() + " is not a file");
        if (dest.isDirectory()) {
            dest = new File(dest, this.name());
        }
        console.log("copying", this.path(), "to", dest.path());
        Files_copyFile(this.path(), dest.path());
        return dest;
    };

    File.prototype.read = function () {
        return Files_read(this.path());
    };

    File.prototype.write = function (text, append) {
        Files_write(this.path(), text, append || false);
    };

    File.prototype.toString = function () {
        return "file(" + this.path() + ")";
    };
})({});
